Visualizing Hurricane Ian Using hvPlot#
Within this post, we explore how we can use hvPlot to plot data from Hurricane Ian, which will soon make landfall in Florida. By the end of this post, you will be able to recreate the following plot:

Imports#
import cartopy.crs as ccrs
import geopandas as gpd
import fiona
from siphon.simplewebservice.ndbc import NDBC
import hvplot.pandas
fiona.drvsupport.supported_drivers['libkml'] = 'rw' # enable KML support which is disabled by default
fiona.drvsupport.supported_drivers['LIBKML'] = 'rw' # enable KML support which is disabled by default
Access the Data#
We are using a few different datasets here. Let’s start with the hurricane forecast from the National Hurricane Center, accessible from their Geographic Information Systems (GIS) webpage:
We need to select the kmz files (a Google GIS file standard for the Hurricane Ian cone and track.
hurricane_cone = gpd.read_file("../data/AL092022_CONE_latest.kmz")
hurricane_track = gpd.read_file("../data/AL092022_TRACK_latest.kmz")
hurricane_track = hurricane_track.dropna(subset=["TCInitLocation"])
Investigate the GeoDataframe#
Let’s check out the geodataframe. Notice how it looks like a typical dataframe, with additional geometry information.
hurricane_cone
| Name | description | timestamp | begin | end | altitudeMode | tessellate | extrude | visibility | drawOrder | ... | stormType | advisoryDate | basin | fcstpd | storm | atcfid | advisoryNum | stormNum | stormName | geometry | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | None | None | NaT | NaT | NaT | None | -1 | 0 | -1 | None | ... | HU | 800 PM EDT Tue Sep 27 2022 | AL | 120 | Hurricane Ian | AL092022 | 19A | 9 | Ian | POLYGON Z ((-83.04900 24.14015 0.00000, -83.02... |
1 rows × 22 columns
hurricane_cone.geometry.plot();
Access NOAA Buoy Data#
The National Oceanic and Atmospheric Administation (NOAA) has a buoy dataset (from the National Data Buoy Center), which includes observations from around the world. We can access this data using siphon, a tool developed by the Unidata Program Center which makes accessing this dataset much easier.
We can use the .lastest_observations() method from the NDBC module to access the latest data.
buoy_df = NDBC.latest_observations()
buoy_df
| station | latitude | longitude | wind_direction | wind_speed | wind_gust | wave_height | dominant_wave_period | average_wave_period | dominant_wave_direction | pressure | 3hr_pressure_tendency | air_temperature | water_temperature | dewpoint | visibility | water_level_above_mean | time | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 13009 | 8.000 | -38.000 | 224.0 | 3.1 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 28.4 | NaN | NaN | NaN | NaN | 2022-09-27 23:00:00+00:00 |
| 1 | 15002 | 0.000 | -10.000 | 156.0 | 6.0 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 24.8 | 25.3 | NaN | NaN | NaN | 2022-09-27 22:00:00+00:00 |
| 2 | 22101 | 37.240 | 126.020 | 120.0 | 3.0 | NaN | 0.0 | 11.0 | NaN | NaN | NaN | NaN | 21.1 | 21.1 | NaN | NaN | NaN | 2022-09-28 00:00:00+00:00 |
| 3 | 22102 | 34.790 | 125.780 | 160.0 | 3.0 | NaN | 0.0 | 11.0 | NaN | NaN | NaN | NaN | 19.8 | 20.6 | NaN | NaN | NaN | 2022-09-28 00:00:00+00:00 |
| 4 | 22103 | 34.000 | 127.500 | 60.0 | 5.0 | NaN | 0.5 | 4.0 | NaN | NaN | NaN | NaN | 22.5 | 23.4 | NaN | NaN | NaN | 2022-09-28 00:00:00+00:00 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 886 | WYCM6 | 30.326 | -89.326 | 10.0 | 6.7 | 8.2 | NaN | NaN | NaN | NaN | 1017.1 | 1.0 | 27.4 | 26.2 | NaN | NaN | NaN | 2022-09-28 00:00:00+00:00 |
| 887 | YATA2 | 59.548 | -139.733 | NaN | NaN | NaN | NaN | NaN | NaN | NaN | 1012.9 | 0.4 | NaN | 12.0 | NaN | NaN | NaN | 2022-09-28 00:00:00+00:00 |
| 888 | YKRV2 | 37.251 | -76.342 | 320.0 | 3.6 | 4.1 | NaN | NaN | NaN | NaN | 1015.4 | 2.1 | 21.8 | NaN | NaN | NaN | NaN | 2022-09-28 00:00:00+00:00 |
| 889 | YKTV2 | 37.227 | -76.479 | 290.0 | 2.1 | 3.1 | NaN | NaN | NaN | NaN | 1015.0 | 2.2 | 20.7 | 23.6 | NaN | NaN | NaN | 2022-09-28 00:00:00+00:00 |
| 890 | YRSV2 | 37.414 | -76.712 | 250.0 | 1.0 | NaN | NaN | NaN | NaN | NaN | 1015.0 | NaN | 17.6 | NaN | 9.4 | NaN | NaN | 2022-09-27 23:30:00+00:00 |
891 rows × 18 columns
Filter the Dataset#
We are interested in locations that have water_temperature values, so we filter using .dropna()
buoy_df = buoy_df.dropna(subset=["water_temperature"])
buoy_df.plot.scatter(x='longitude', y='latitude', c='water_temperature')
<AxesSubplot: xlabel='longitude', ylabel='latitude'>
Plot our Data using hvPlot#
Let’s move to interactive visualization!
Plot the Hurricane Cone and Track Using hvPlot#
We can do better than just static plots - let’s create an interactive one using hvPlot!
hurricane_cone.hvplot(color='None',
line_width=3,
coastline=True,
xlim=(-95, -65),
ylim=(20, 40),
label='NHC Forecast Cone for Hurricane Ian',
projection=ccrs.PlateCarree(),
features=["land", "ocean"])
And the same for the best estimated track
hurricane_track_plot = hurricane_track.hvplot(line_color='Red',
color="None",
line_width=3,
coastline=True,
xlim=(-95, -65),
ylim=(20, 40),
label='NHC Forecast Track for Hurricane Ian',
projection=ccrs.PlateCarree(),
features=["land", "ocean"])
hurricane_track_plot
We still need a title though, since this does not tell us what time this is valid… this information is in the dataframe!
hurricane_cone.advisoryDate
0 800 PM EDT Tue Sep 27 2022
Name: advisoryDate, dtype: object
Combine our Forecast Cone and Track Plots#
Let’s add the titles, and combine our plots.
hurricane_track_plot = hurricane_track.hvplot(line_color='Red',
color="None",
line_width=3,
coastline=True,
xlim=(-95, -65),
ylim=(20, 40),
title=f'NHC Forecast Valid: {hurricane_track.pubAdvTime.values[0]}',
label='NHC Forecast Track for Hurricane Ian',
projection=ccrs.PlateCarree()
)
hurricane_cone_plot = hurricane_cone.hvplot(color='None',
line_width=3,
line_color='Black',
coastline=True,
xlim=(-95, -65),
ylim=(20, 40),
title=f"NHC Forecast Valid {hurricane_cone.advisoryDate.values[0]}",
label='NHC Forecast Cone for Hurricane Ian',
projection=ccrs.PlateCarree(),
features=["land", "ocean"])
hurricane_plot = (hurricane_cone_plot * hurricane_track_plot)
hurricane_plot
Plot the Buoy Data using hvPlot#
buoy_plot = buoy_df.hvplot.points(x='longitude',
y='latitude',
c='water_temperature',
cmap='inferno',
label='NOAA Buoy Locations',
title='NOAA Buoy Water Temperature',
clabel='Temperature (degC)',
geo=True,
coastline=True,
projection=ccrs.PlateCarree(),
xlim=(-95, -65),
ylim=(20, 40),
clim=(20, 35))
buoy_plot
Final Visualization#
Let’s put it all together! We combine our plots using the * operator.
hurricane_plot * buoy_plot
Conclusions#
Within this example, we explored visualizing data from the National Hurricane Center, and from the National Data Buoy Center. We encourage you to try this out on your own.
In the future, it would be nice to add other datasets, such as weather radar data, onto these plots as well.